<?php
/* SVN FILE: $Id: tagable.php 1839 2008-10-22 12:10:13Z francis $ */
/**
 * Tagable Behavior
 *
 * This behavior allows processing of tags and generates tagclouds.
 */
class TagableBehavior extends ModelBehavior {
/**
 * Settings array
 *
 * @var array
 * @access public
 */
	var $settings = array();
/**
 * Default settings
 *
 * separator				- sperator used to enter a lot of tags, comma by default
 * tagClass					- class name of the table storing the tags
 * field					- the fieldname that contains the raw tags as string
 * joinTable 				- name of the join table between tags and model(s)
 * foreignKey				- foreignKey used in the HABTM association
 * associationForeignKey	- associationForeignKey used in the HABTM association
 * automticTagging			- if set to true you dont need to use saveTags() manualy
 *
 * @var array
 * @access public
 */
	var $defaults = array(
		'separator' => ',',
		'field' => 'tags',
		'tagClass' => 'Tag',
		'joinTable' => 'tagged',
		'foreignKey' => 'foreign_key',
		'associationForeignKey' => 'tag_id',
		'automticTagging' => true);
/**
 * Setup
 *
 * @param AppModel $model
 * @param array $settings
 */
	function setup(&$model, $settings = array()) {
		if (!isset($this->settings[$model->name])) {
			$this->settings[$model->name] = $this->defaults;
		}
		$this->settings[$model->name] = am($this->settings[$model->name], ife(is_array($settings), $settings, array()));

		$model->bindModel(array('hasAndBelongsToMany' => array(
			'Tag' => array(
				'className' => $this->settings[$model->name]['tagClass'],
				'joinTable' => $this->settings[$model->name]['joinTable'],
				'foreignKey' => $this->settings[$model->name]['foreignKey'],
				'associationForeignKey' => $this->settings[$model->name]['associationForeignKey'],
				'unique' => true,
				'conditions' => '',
				'fields' => '',
				'dependent' => true))), false);
	}
/**
 * Generates array of Tag IDs and fixes string passed by reference
 *
 * Example usage (before saving record):
 * $this->data['Tag']['Tag'] = $this->Blog->saveTags($this->data['Blog']['tags']);
 * where form input Media.tags is text field with comma-delimited list of tags
 *
 * @param AppModel $model
 * @param string $string
 * @return array
 */
	function saveTags(&$model, &$string) {
		$result = array();
		$tagClass = $this->settings[$model->name]['tagClass'];
		$tagModel = $model->{$tagClass};

		if ($string) {
			$tags = array();
			$array = explode(',', $string);

			foreach ($array as $tag) {
				$identifier = null;
				$tag = trim($tag);

				if (strpos($tag, ':') !== false) {
					$t = explode(':', $tag);
					$identifier = trim($t[0]);
					$tag = trim($t[1]);
				}

				if (!empty($tag)) {
					$tags[] = $tag;

					$this->data[$tagClass]['name'] = $tag;
					$this->data[$tagClass]['identifier'] = $identifier;
					$this->data[$tagClass]['keyname'] = preg_replace('/[^a-z0-9]/', '', low($tag));

					$tagModel->recursive = -1;

					$existing = $tagModel->find(array('keyname' => $this->data[$tagClass]['keyname']));

					if(!empty($existing)) {
						$result[] = $existing[$tagClass][$tagModel->primaryKey];
					} else {
						$tagModel->id = null;

						if($tagModel->save($this->data)) {
							$result[] = $tagModel->id;
						}
					}
				}
			}
		}
		$result = array_unique($result);
		return $result;
	}
/**
 * Generates comma-delimited string of tag names from tag array(), needed for
 * initialization of data for text input
 *
 * Example usage (only 'Tag.name' field is needed inside of method):
 * $this->Blog->hasAndBelongsToMany['Tag']['fields'] = array('name', 'keyname');
 * $blog = $this->Blog->read(null, 123);
 * $blog['Blog']['tags'] = $this->Blog->Tag->prepare($blog['Tag']);
 *
 * @param string $string
 * @return array
 */
	function prepare(&$model, $data = null) {
		if ($data) {
			return join($this->settings[$model->name]['separator'].' ', Set::extract($data, '{n}.name'));
		}
		return '';
	}
/**
 * Generates the so called tag cloud
 *
 * @param AppModel $model
 * @param string $id
 */
	function getTagCloud(&$model, $id = null) {
		if ($id === null && isset($model->id)) {
			$id = $model->id;
		}
		$cloud = $model->query("SELECT `Tag`.`id` , `Tag`.`name` , `Tag`.`keyname` , COUNT(tagged.foreign_key) `count`
			FROM `tagged`
				JOIN `tags` AS `Tag` ON `tagged`.`tag_id` = `Tag`.`id`
				JOIN `".$model->useTable."` AS `".$model->alias."` ON `tagged`.`foreign_key` = `".$model->alias."`.`id`
			WHERE `".$model->alias."`.`id` = \"$id\"
			GROUP BY (tag_id)
			ORDER BY RAND() , `Tag`.`name` ASC
			LIMIT 30");

		foreach ($cloud as $key => $tag) {
			$cloud[$key]['Tag']['weight'] = $tag[0]['count'];
			unset($cloud[$key][0]);
		}
		return $cloud;
	}
/**
 * beforeSave callback
 *
 * @param AppModel $model
 */
	function beforeSave(&$model) {
		if ($this->settings[$model->name]['automticTagging'] == true) {
			$model->data['Tag']['Tag'] = $this->saveTags($model, $model->data[$model->name][$this->settings[$model->name]['field']]);
		}
	}
/**
 * beforeSave callback
 *
 * @param AppModel $model
 * @param array $results
 * @param mixed
 * @return array
 */
	function afterFind(&$model, $results, $primary) {
		extract($this->settings[$model->name]);
		foreach ($results as $key => $row) {

			if (isset($row[$tagClass]) && !empty($row[$tagClass])) {
				$row[$model->name][$field] = $this->prepare($model, $row[$tagClass]);
				unset($row[$tagClass]);
				$results[$key] = $row;
			}
		}
		return $results;
	}
}
?>